﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Printing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ICSharpCode.WinFormsUI.Controls
{
    public class NDataGridView : System.Windows.Forms.DataGridView
    {
        private bool dgvScroll = false;
        private string theme_name = "Default";
        private string editing_model = "undefined";
        private DataGridViewPrinter MyDataGridViewPrinter;
        private System.Drawing.Printing.PrintDocument MyPrintDocument;
       
        private int _displayWidth = 0;
        private int _displayRowCount = 0;       
        private NHScrollBar _hScrollBarEx;
        private NVScrollBar _vScrollBarEx;
        
        private bool _dataFormated = false;         

        public NDataGridView()
        {            
            InitializeControl();
        }

        #region 属性

        private bool _ShowRowNumber;
        /// <summary>
        /// 是否显示行号
        /// </summary>
        public bool ShowRowNumber
        {
            get { return _ShowRowNumber; }
            set { _ShowRowNumber = value; }
        }
        
        private object _DataSource;
        /// <summary>
        /// 
        /// </summary>
        public new object DataSource
        {
            get
            {
                return _DataSource;
            }
            set
            {
                _DataSource = value;
                BindDataSource();
            }
        }

        private string _datetimeFormat = "";
        /// <summary>
        /// 日期时间格式
        /// </summary>
        public string DatetimeFormat
        {
            get { return _datetimeFormat; }
            set { _datetimeFormat = value; }
        }

        public ShowToastValue ShowToastValue;

        #endregion

        protected void InitializeControl()
        {
            this.ShowRowNumber = false;
            this.DoubleBuffered = true;
            this.EnableHeadersVisualStyles = false;

            _hScrollBarEx = new NHScrollBar();
            _vScrollBarEx = new NVScrollBar();
            _datetimeFormat = "yyyy-MM-dd HH:mm:ss";

            this.Controls.Add(_hScrollBarEx);
            this.Controls.Add(_vScrollBarEx);

            base.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);

            this.SizeChanged += new EventHandler(ScrollBar_VisibleChanged);
            this.HorizontalScrollBar.VisibleChanged += new EventHandler(ScrollBar_VisibleChanged);
            this.VerticalScrollBar.VisibleChanged += new EventHandler(ScrollBar_VisibleChanged);

            this._vScrollBarEx.ValueChanged += _vScrollBarEx_ValueChanged;
            this._hScrollBarEx.ValueChanged += _hScrollBarEx_ValueChanged;
            this.Scroll += new ScrollEventHandler(DataGridViewEx_Scroll);

            this.ColumnHeadersHeightChanged += new EventHandler(ScrollBar_VisibleChanged);
            this.ColumnWidthChanged += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);
            this.RowHeadersWidthChanged += new EventHandler(ScrollBar_VisibleChanged);
            this.RowHeightChanged += new DataGridViewRowEventHandler(ScrollBar_VisibleChanged);
            this.RowsAdded += new DataGridViewRowsAddedEventHandler(ScrollBar_VisibleChanged);
            this.RowsRemoved += new DataGridViewRowsRemovedEventHandler(ScrollBar_VisibleChanged);
            this.ColumnAdded += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);
            this.ColumnRemoved += new DataGridViewColumnEventHandler(ScrollBar_VisibleChanged);
            this.DataSourceChanged += new EventHandler(ScrollBar_VisibleChanged);

            this.MyPrintDocument = new System.Drawing.Printing.PrintDocument();
            this.MyPrintDocument.PrintPage += OnPrintDocument_PrintPage;

            UpdateScrollBarInformation();
        }

        protected void UpdateScrollBarInformation()
        {
            int borderWidth = 1;        

            if (this.VerticalScrollBar.Visible)
            {
                _vScrollBarEx.Visible = true;

                this.VerticalScrollBar.SendToBack();

                _vScrollBarEx.Width = ScrollBarInformation.VerticalScrollBarWidth;
                _vScrollBarEx.Height = _hScrollBarEx.Visible ? this.Height - this._hScrollBarEx.Height - 2 * borderWidth + 1 :
                 this.Height - 2 * borderWidth;

                _vScrollBarEx.Location = new Point(this.Width - _vScrollBarEx.Width - borderWidth, borderWidth);

                GetDisplayRowCount();

            }
            else
            {
                _vScrollBarEx.Visible = false;
            }

            if (this.HorizontalScrollBar.Visible)
            {
                _hScrollBarEx.Visible = true;

                this.HorizontalScrollBar.SendToBack();

                _hScrollBarEx.Width = _vScrollBarEx.Visible ? this.Width - this._vScrollBarEx.Width - 2 * borderWidth + 1 :
                  this.Width - 2 * borderWidth;
                _hScrollBarEx.Height = ScrollBarInformation.HorizontalScrollBarHeight;
                _hScrollBarEx.Location = new Point(borderWidth, this.Height - _hScrollBarEx.Height - borderWidth);

                GetDisplayWidth();

                _hScrollBarEx.Value = this.HorizontalScrollingOffset;

            }
            else
            {
                _hScrollBarEx.Visible = false;
            }           
        }

        private void _vScrollBarEx_ValueChanged(object sender, EventArgs e)
        {
            try
            {
                if (this._vScrollBarEx.Value <= this._vScrollBarEx.Maximum)
                {
                    this.FirstDisplayedScrollingRowIndex = this._vScrollBarEx.Value;
                }
            }
            catch
            {

            }
        }

        private void _hScrollBarEx_ValueChanged(object sender, EventArgs e)
        {
            this.HorizontalScrollingOffset = this._hScrollBarEx.Value;
        }        

        private void DataGridViewEx_Scroll(object sender, ScrollEventArgs e)
        {
            this.dgvScroll = true;
            if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
            {
                _vScrollBarEx.Value = this.FirstDisplayedScrollingRowIndex;
            }
            else
            {
                _hScrollBarEx.Value = this.HorizontalScrollingOffset;
            }
            this.Invalidate();
            this.dgvScroll = false;
        }

        private void ScrollBar_VisibleChanged(object sender, EventArgs e)
        {
            UpdateScrollBarInformation();
        }

        protected override void OnDataError(bool displayErrorDialogIfNoHandler, System.Windows.Forms.DataGridViewDataErrorEventArgs e)
        {
            //base.OnDataError(displayErrorDialogIfNoHandler, e);
            if (e.RowIndex > -1 && e.ColumnIndex > -1)
            {
                DataGridViewCell cell = this.Rows[e.RowIndex].Cells[e.ColumnIndex];
                DoDataError(cell, e.Context);
                cell.ErrorText = e.Exception.Message;
            }
        }

        protected override void OnCellFormatting(DataGridViewCellFormattingEventArgs e)
        {
            base.OnCellFormatting(e);
            if (Type.Equals(e.DesiredType, typeof(System.Drawing.Image)))
            {
                if (e.RowIndex > -1 && e.ColumnIndex > -1)
                {
                    e.Value = DoDataImage(e.Value, e.RowIndex, e.ColumnIndex);
                }
            }                      
        }

        protected override void OnCellClick(DataGridViewCellEventArgs e)
        {
            base.OnCellClick(e);
            if (this.ShowToastValue != null)
            {
                if (e.RowIndex > -1 && e.ColumnIndex > -1)
                {
                    DataGridViewCell cell = this.Rows[e.RowIndex].Cells[e.ColumnIndex];
                    if (cell.FormattedValueType.Equals(typeof(System.Drawing.Image)) && cell.ValueType.Equals(typeof(Byte[])))
                    {
                        var Value = cell.Value;
                        try
                        {
                            Byte[] array = (Byte[])Value;
                            using (System.IO.Stream stream = new System.IO.MemoryStream(array))
                            {
                                //stream.Read(array, 0, array.Length);
                                Image image = (Bitmap)Image.FromStream(stream, true);
                            }
                        }
                        catch
                        {
                            this.ShowToastValue(this, new ToastEventArgs() { Value = cell.Value });
                        }                        
                    }
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DrawScrollBarsBox(e.Graphics);
        }

        protected override void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e)
        {
            base.OnRowPostPaint(e);           
            if (this._ShowRowNumber)
            {
                if (e.RowIndex > -1)
                {
                    System.Drawing.Rectangle rectangle =
                    new System.Drawing.Rectangle(e.RowBounds.Location.X,
                                     e.RowBounds.Location.Y,
                                     this.RowHeadersWidth - 4,
                                     e.RowBounds.Height);

                    TextRenderer.DrawText(e.Graphics, (e.RowIndex + 1).ToString(),
                        this.RowHeadersDefaultCellStyle.Font,
                        rectangle,
                        this.RowHeadersDefaultCellStyle.ForeColor,
                        TextFormatFlags.VerticalCenter | TextFormatFlags.Right);
                }                 
            }
        }

        protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
        {
            base.OnCellPainting(e);
            if (e.RowIndex == -1)
            {
                OnRedrawCell(this, e);
            }
            if (e.RowIndex > -1 && e.ColumnIndex == -1)
            {
                OnRedrawCell(this, e);
            }
        }

        protected override void OnCellBeginEdit(DataGridViewCellCancelEventArgs e)
        {            
            base.OnCellBeginEdit(e);
            this.editing_model = "start_edit";
        }

        protected override void OnCellEndEdit(DataGridViewCellEventArgs e)
        {            
            base.OnCellEndEdit(e);
            this.editing_model = "end_edit";
        }

        protected virtual Image DoDataImage(object Value, int RowIndex, int ColumnIndex)
        {
            int width = 100; int height = 22;
            Bitmap image = new Bitmap(width, height);

            try
            {
                Byte[] array = (Byte[])Value;
                using (System.IO.Stream stream = new System.IO.MemoryStream(array))
                {
                    //stream.Read(array, 0, array.Length);
                    image = (Bitmap)Image.FromStream(stream, true);
                }
            }
            catch (Exception ex)
            {
                if (this.Columns[ColumnIndex].Resizable != DataGridViewTriState.False)
                {
                    this.Columns[ColumnIndex].Resizable = DataGridViewTriState.False;
                }

                var cell = this.Rows[RowIndex].Cells[ColumnIndex];
                image = new Bitmap(cell.OwningColumn.Width, cell.OwningRow.Height);

                using (Graphics graphic = Graphics.FromImage(image))
                {
                    using (Brush _bru = new SolidBrush(cell.Style.BackColor))
                    {
                        graphic.FillRectangle(_bru, new Rectangle(0, 0, image.Width, image.Height));
                    }

                    if (Value != null && Value != DBNull.Value)
                    {
                        StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Center };
                        string strValue = string.Empty;

                        strValue = "Byte[] 字节流";

                        if (!string.IsNullOrEmpty(strValue) && strValue.Length > 16)
                            strValue = string.Concat(strValue.Substring(0, 16), "...");

                        graphic.DrawString(strValue, this.Font, new SolidBrush(DefaultCellStyle.ForeColor), new Rectangle(0, 0, image.Width, image.Height), sf);
                    }

                }
            }

            return image;
        }

        protected virtual void DoDataError(DataGridViewCell cell, DataGridViewDataErrorContexts context)
        {
            //if (cell.FormattedValue != null) //&& cell.FormattedValue.GetType().Equals(typeof(System.Drawing.Bitmap))
            //{
            //    Bitmap image = new Bitmap(cell.OwningColumn.Width, cell.OwningRow.Height);
            //    Graphics graphic = Graphics.FromImage(image);
            //    using (Brush _bru = new SolidBrush(cell.Style.BackColor))
            //    {
            //        graphic.FillRectangle(_bru, new Rectangle(0, 0, image.Width, image.Height));
            //    }

            //    var Value = cell.Value;
            //    if (Value != null && Value != DBNull.Value)
            //    {
            //        StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Center };
            //        string strValue = System.Text.Encoding.Default.GetString((Byte[])Value);
            //        graphic.DrawString(strValue, this.Font, Brushes.Black, new Rectangle(0, 0, image.Width, image.Height), sf);
            //    }

            //    graphic.Dispose();

            //    cell.Value = image;
            //}            
        }

        private void DrawScrollBarsBox(Graphics g)
        {
            Rectangle r1 = DisplayRectangle;
            Rectangle r2 = ClientRectangle;
            Color fillBackcolor = SystemColors.Control;
            switch (theme_name)
            {
                case "Black":
                    fillBackcolor = Color.FromArgb(030, 030, 030);
                    break;
                case "Default":
                    fillBackcolor = Color.White;
                    break;
            }
            g.FillRectangle(new SolidBrush(fillBackcolor),
                new Rectangle(r1.Right - 1, r1.Bottom - 1, r2.Width - r1.Width, r2.Height - r1.Height));
        }

        private void OnPrintDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            bool hasMorePages = MyDataGridViewPrinter.DrawDataGridView(e.Graphics);
            if (hasMorePages == true)
                e.HasMorePages = true;
        }

        private void OnRedrawCell(object sender,DataGridViewCellPaintingEventArgs e)
        {
            DataGridViewCellStyle headCellStyle = this.ColumnHeadersDefaultCellStyle;
            using (Brush brush = new SolidBrush(headCellStyle.BackColor))
            {
                e.Graphics.FillRectangle(brush, e.CellBounds);
                Rectangle border = e.CellBounds;
                border.Offset(new Point(-1, -1));
                e.Graphics.DrawRectangle(Pens.Gray, border);
            }
            e.PaintContent(e.CellBounds);
            e.Handled = true;
        }

        public int GetDisplayWidth()
        {
            int width = 0;
            for (int i = 0; i < this.Columns.Count; i++)
            {
                width += this.Columns[i].Width;
            }
            _displayWidth = width;
            this._hScrollBarEx.Maximum = width;
            this._hScrollBarEx.LargeChange = _hScrollBarEx.Maximum / _hScrollBarEx.Width + this.DisplayRectangle.Width - this.RowHeadersWidth;
            return width;
        }

        public int GetDisplayRowCount()
        {
            int j = 0;
            int height = 0;
            int i = this.FirstDisplayedScrollingRowIndex;
            if (i < 0)
            {
                i = 0;
            }
            for (; i < this.Rows.Count; i++)
            {
                height += this.Rows[i].Height;
                if (height < this.DisplayRectangle.Height - this.ColumnHeadersHeight)
                {
                    j++;
                }
                else
                {
                    break;
                }
            }

            j = this.Rows.Count - j;
            if (j < 0)
            {
                j = 0;
            }

            if (_displayRowCount != j)
            {
                _displayRowCount = j;

                //_vScrollBarEx.Maximum = _displayRowCount + _vScrollBarEx.LargeChange + 2;
                //_vScrollBarEx.Maximum = _displayRowCount + _vScrollBarEx.Minimum + _vScrollBarEx.LargeChange + 2;
                _vScrollBarEx.Maximum = _displayRowCount + _vScrollBarEx.Minimum + 2;

                if (this.FirstDisplayedScrollingRowIndex < 0)
                {
                    _vScrollBarEx.Value = 0;
                }
                else if (this.FirstDisplayedScrollingRowIndex > _vScrollBarEx.Maximum)
                {
                    _vScrollBarEx.Value = _vScrollBarEx.Maximum;
                }
                else
                {
                    _vScrollBarEx.Value = this.FirstDisplayedScrollingRowIndex;
                }
            }

            return j;

        }
        
        protected virtual bool SetupThePrinting()
        {
            PrintDialog MyPrintDialog = new PrintDialog();
            MyPrintDialog.AllowCurrentPage = false;
            MyPrintDialog.AllowPrintToFile = false;
            MyPrintDialog.AllowSelection = false;
            MyPrintDialog.AllowSomePages = false;
            MyPrintDialog.PrintToFile = false;
            MyPrintDialog.ShowHelp = false;
            MyPrintDialog.ShowNetwork = false;

            if (MyPrintDialog.ShowDialog() != DialogResult.OK)
                return false;

            MyPrintDocument.DocumentName = "Customers Report";
            MyPrintDocument.PrinterSettings = MyPrintDialog.PrinterSettings;
            MyPrintDocument.DefaultPageSettings = MyPrintDialog.PrinterSettings.DefaultPageSettings;
            MyPrintDocument.DefaultPageSettings.Margins = new Margins(40, 40, 40, 40);

            MyDataGridViewPrinter = new DataGridViewPrinter(this, MyPrintDocument, true, true, "数据报表", new Font("Tahoma", 18, FontStyle.Bold, GraphicsUnit.Point), Color.Black, true);

            return true;
        }
                
        protected virtual void BindDataSource()
        {
            base.DataSource = BindingDataHook(_DataSource);           
        }

        protected virtual object BindingDataHook(object dataSource)
        {
            if (DataSource.GetType().Equals(typeof(System.Data.DataTable)))
            {
                Boolean isHaveArrayColumn = false;
                DataTable dataTable = (DataTable)dataSource;
                DataTable newTable = new DataTable();
                foreach (DataColumn dc in dataTable.Columns)
                {
                    if (dc.DataType.IsArray || dc.DataType.IsGenericType)
                    {
                        isHaveArrayColumn = true;
                        newTable.Columns.Add(new DataColumn(dc.ColumnName, typeof(string)));
                    }
                    else if (dc.DataType.FullName.Equals("System.Array"))
                    {
                        isHaveArrayColumn = true;
                        newTable.Columns.Add(new DataColumn(dc.ColumnName, typeof(string)));
                    }
                    else
                    {
                        newTable.Columns.Add(new DataColumn(dc.ColumnName, dc.DataType));
                    }
                }
                if (isHaveArrayColumn)
                {
                    foreach(DataRow dr in dataTable.Rows)
                    {
                        DataRow newRow = newTable.NewRow();
                        foreach(DataColumn dc in dataTable.Columns)
                        {
                            if (dc.DataType.IsArray || dc.DataType.IsGenericType)
                            {
                                string cellValue = "{}";
                                if (dc.DataType.Equals(typeof(string[])))
                                {
                                    cellValue = "{"+ string.Join(",", (string[])dr[dc.ColumnName]) + "}";
                                }
                                else if (dc.DataType.Equals(typeof(int[])))
                                {
                                    cellValue = "{" + string.Join(",", (int[])dr[dc.ColumnName]) + "}";
                                }
                                newRow[dc.ColumnName] = cellValue;
                            }
                            else if (dc.DataType.FullName.Equals("System.Array"))
                            {
                                string cellValue = "{}";
                                object cellObjectValue= dr[dc.ColumnName];
                                Type cellObjectType = cellObjectValue.GetType();
                                if (cellObjectType.Equals(typeof(string[])))
                                {
                                    cellValue = "{" + string.Join(",", (string[])cellObjectValue) + "}";
                                }
                                else if (cellObjectType.Equals(typeof(int[])))
                                {
                                    cellValue = "{" + string.Join(",", (int[])cellObjectValue) + "}";
                                }
                                newRow[dc.ColumnName] = cellValue;
                            }
                            else
                            {
                                newRow[dc.ColumnName] = dr[dc.ColumnName];
                            }
                        }
                        newTable.Rows.Add(newRow);
                    }

                    return newTable;
                }
            }
            return dataSource;
        }

        protected virtual void BindingDataCellFormat()
        {
            if (_dataFormated)
                return;

            _dataFormated = true;

            foreach (DataGridViewColumn column in this.Columns)
            {
                if (Type.Equals(column.ValueType, typeof(System.DateTime)))
                {
                    if (!column.HasDefaultCellStyle)
                    {
                        column.DefaultCellStyle.Format = _datetimeFormat; //"yyyy-MM-dd HH:mm:ss.fff";
                    }
                }
            }
        }

        protected override void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e)
        {
            base.OnDataBindingComplete(e);
            BindingDataCellFormat();
        }

        public void SetTheme(string Theme)
        {
            DataGridViewCellStyle DefaultCellStyle = new DataGridViewCellStyle();
            DataGridViewCellStyle ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle();

            switch (Theme)
            {
                case "Default":

                    _vScrollBarEx.BorderColor = SystemColors.ControlLight;
                    _hScrollBarEx.BorderColor = SystemColors.ControlLight;
                    _vScrollBarEx.ScrollBarRenderer = new WhiteScrollBarRenderer();
                    _hScrollBarEx.ScrollBarRenderer = new WhiteScrollBarRenderer();

                    ColumnHeadersDefaultCellStyle.BackColor = SystemColors.Control;
                    ColumnHeadersDefaultCellStyle.Padding = new Padding(4, 4, 4, 4);
                    ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;

                    DefaultCellStyle.BackColor = Color.White;
                    DefaultCellStyle.ForeColor = Color.Black;

                    this.BackgroundColor = Color.White;

                    break;
                case "Black":

                    _vScrollBarEx.BorderColor = Color.FromArgb(93, 140, 201);
                    _hScrollBarEx.BorderColor = Color.FromArgb(93, 140, 201);
                    _vScrollBarEx.ScrollBarRenderer = new BlackScrollBarRenderer();
                    _hScrollBarEx.ScrollBarRenderer = new BlackScrollBarRenderer();

                    ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(030, 030, 030);
                    ColumnHeadersDefaultCellStyle.Padding = new Padding(4, 4, 4, 4);
                    ColumnHeadersDefaultCellStyle.ForeColor = Color.White;

                    DefaultCellStyle.BackColor = Color.FromArgb(062, 062, 062);
                    DefaultCellStyle.ForeColor = Color.White;

                    this.BackgroundColor = Color.FromArgb(030, 030, 030);

                    break;
            }

            this.theme_name = Theme;
            this.ColumnHeadersDefaultCellStyle = ColumnHeadersDefaultCellStyle;
            this.RowHeadersDefaultCellStyle.ForeColor = ColumnHeadersDefaultCellStyle.ForeColor;
            this.DefaultCellStyle = DefaultCellStyle;
        }

        public void PrintPreview()
        {
            if (SetupThePrinting())
            {
                PrintPreviewDialog MyPrintPreviewDialog = new PrintPreviewDialog();                
                MyPrintPreviewDialog.Document = MyPrintDocument;
                MyPrintPreviewDialog.ShowDialog();
            }
        }

        public void Print()
        {
            if (SetupThePrinting())
                MyPrintDocument.Print();
        }

    }

    public class ToastEventArgs : System.EventArgs
    {
        public object Value { get; set; }
    }

    public delegate void ShowToastValue(object sender,ToastEventArgs e);
}
